"""
基础小部件。
"""
import abc
import typing
from abc import ABC

from ..framework import dispatch
from ..utils import Any, Empty, callback, BoolValue, Int32Value, from_any, to_any


class BaseWidget(metaclass=abc.ABCMeta):
    """
    基础小部件类。
    """
    width: int = 0
    height: int = 0

    async def on_widget_create(self, *argv):
        self.width = from_any(argv[0], Int32Value)
        self.height = from_any(argv[1], Int32Value)

    async def on_widget_action(self, *argv, name: typing.Optional[str] = None, session_id: int = 0, future_id: int = 0):
        """
        组件的事件转发。
        """
        if name == 'onCreate':
            await self.on_widget_create(*argv)
        elif name == 'onGetRegion':
            dispatch(self.on_get_region(*argv, session_id=session_id, future_id=future_id))
        elif name == 'onGetAccessibilityLabel':
            dispatch(self.on_get_accessibility_label(*argv, session_id=session_id, future_id=future_id))
        elif name == 'onIsEnabled':
            dispatch(self.on_is_enabled(*argv, session_id=session_id, future_id=future_id))

    @staticmethod
    def parse_region(*args: typing.Any) -> typing.Tuple:
        """
        解析一个区域参数。
        把(width, height, x, y)转成可传输的参数数据。
        """
        if len(args) < 2:
            raise TypeError('Missing 1 required positional argument: `width` and `height`.')
        if len(args) < 4:
            raise TypeError('Missing 3 required positional argument: `x` and `y`.')

        width, height, x, y = [to_any(args[i], Int32Value) for i in range(4)]
        return width, height, x, y

    @callback(int, int, int, int)
    async def on_get_region(self, width, height, x, y, session_id=0, future_id=0):
        """
        当获取小部件大小和位置成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, (width, height, x, y))

    async def click(self):
        """
        小部件执行点击操作。
        调用示例：
        await widget.click()
        """
        dispatch(self.send_action('click'))

    async def focus(self):
        """
        小部件聚焦。
        调用示例：
        await widget.focus()
        """
        dispatch(self.send_action('focus'))

    async def get_region(self) -> typing.Tuple[int]:
        """
        获取小部件的大小和位置（返回width、height、x和y）
        """
        return await self.send_action_until_return('getRegion')

    async def set_on_click_listener(self, func: typing.Optional[typing.Callable] = None):
        """
        设置小部件被点击时的监听函数。
        :param func: 一个可调用的无参异步函数对象，如果是None则取消监听函数。
        调用示例：
        async def cb(): ...
        await widget.set_on_click_listener(cb)
        """
        dispatch(self.send_action('setOnClickListener', self.create_callback(func)))

    async def set_on_long_click_listener(self, func: typing.Optional[typing.Callable] = None):
        """
        设置小部件被长按时的监听函数。
        :param func: 一个可调用的无参异步函数对象，如果是None则取消监听函数。
        调用示例：
        async def cb(): ...
        await widget.set_on_long_click_listener(cb)
        """
        dispatch(self.send_action('setOnLongClickListener', self.create_callback(func)))

    async def set_on_gesture_listener(self, func: typing.Optional[typing.Callable] = None, move_interval: int = 20):
        """
        设置小部件手势事件的监听函数。
        :param func: 一个可调用的异步函数对象（需要实现x: int, y: int, pressed: bool和pointerCount: int四个参数），如果是None则取消监听函数。
        :param move_interval: 当持续按住屏幕在移动手指时，每隔多少像素通知一次。
        调用示例：
        async def cb(x: int, y: int, pressed: bool, pointerCount: int): ...
        await widget.set_on_gesture_listener(cb)
        """
        if not func:
            dispatch(self.send_action('setOnGestureListener', to_any(None), to_any(move_interval, Int32Value)))
            return

        async def _func(x: Any, y: Any, pressed: Any, pointer_count: Any):
            """
            回调监听代理函数。
            :param x: 手势事件当前X坐标。
            :param y: 手势事件当前Y坐标。
            :param pressed: 手指按下状态。
            :param pointer_count: 手指数量。
            """
            if func:
                await func(from_any(x, Int32Value), from_any(y, Int32Value), from_any(pressed, BoolValue),
                           from_any(pointer_count, Int32Value))

        dispatch(
            self.send_action('setOnGestureListener', self.create_callback(_func), to_any(move_interval, Int32Value)))

    @callback(str)
    async def on_get_accessibility_label(self, label, session_id=0, future_id=0):
        """
        当获取小部件无障碍标签成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, label)

    async def get_accessibility_label(self) -> str:
        """
        获取小部件无障碍标签。
        调用示例：
        print(await widget.get_accessibility_label())
        """
        return await self.send_action_until_return('getAccessibilityLabel')

    async def set_accessibility_label(self, label: str):
        """
        给小部件设置无障碍标签。
        :param label: 要显示的小部件文字。
        调用示例：
        await widget.set_accessibility_label('这是无障碍标签')
        """
        dispatch(self.send_action('setAccessibilityLabel', to_any(label)))

    async def set_location(self, x: int, y: int):
        """
        给小部件设置位置（左上角的坐标x,y）。
        :param x: 左上角横坐标；
        :param y: 左上角纵坐标。
        调用示例：
        await widget.set_location(30, 880)
        """
        dispatch(self.send_action('setLocation', to_any(x, Int32Value), to_any(y, Int32Value)))

    async def set_background_color(self, color: typing.Optional[str]):
        """
        给小部件设置背景色。
        :param color: RGB或RGBA的十六进制颜色值。
        调用示例：
        await widget.set_background_color('#FF000000')
        await widget.setBackgroundColor(None)
        """
        dispatch(self.send_action('setBackgroundColor', to_any(color)))

    @callback(bool)
    async def on_is_enabled(self, enabled, session_id=0, future_id=0):
        """
        当获取小部件的启用状态成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, enabled)

    async def is_enabled(self) -> bool:
        """
        获取小部件的启用状态。
        调用示例：
        print(await widget.is_enabled())
        """
        return await self.send_action_until_return('isEnabled')

    async def set_enabled(self, enabled: bool):
        """
        给小部件设置启用状态。
        :param enabled: 启用是True，禁用是False。
        调用示例：
        await widget.set_enabled(True)
        """
        dispatch(self.send_action('setEnabled', to_any(enabled)))

    @abc.abstractmethod
    async def send_action(self, name: str, *argv, session_id: int = 0, future_id: int = 0):
        """
        发送一个交互动作。
        """
        ...

    @abc.abstractmethod
    async def send_action_until_return(self, name: str, *argv) -> typing.Any:
        """
        发送交互动作并等待返回。
        """
        ...

    @abc.abstractmethod
    def set_action_result(self, session_id: int, future_id: int, res):
        """
        设置交互动作的结果。
        """
        ...

    @abc.abstractmethod
    def create_callback(self, func: typing.Optional[typing.Callable], *args) -> typing.Union[Any, Empty]:
        """
        创建一个回调程序。
        """
        ...


class BaseTextWidget(BaseWidget, ABC):
    """
    基础文本小部件类。
    """

    @staticmethod
    def parse_region_and_text(*args: typing.Any) -> typing.Tuple:
        """
        解析一个区域和文字参数。
        把(width, height, x, y, text)转成可传输的参数数据。
        其中text可省略。
        """
        text = to_any(args[4] if len(args) > 4 else None)
        return *BaseWidget.parse_region(*args), text,

    async def on_widget_action(self, *argv, name: typing.Optional[str] = None, session_id: int = 0, future_id: int = 0):
        await super().on_widget_action(*argv, name=name, session_id=session_id, future_id=future_id)
        if name == 'onGetText':
            dispatch(self.on_get_text(*argv, session_id=session_id, future_id=future_id))

    @callback(str)
    async def on_get_text(self, text, session_id=0, future_id=0):
        """
        当获取小部件文字成功后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        if not session_id:
            return
        self.set_action_result(session_id, future_id, text)

    async def get_text(self) -> str:
        """
        获取小部件文字。
        调用示例：
        print(await widget.get_text())
        """
        return await self.send_action_until_return('getText')

    async def set_text(self, text: str):
        """
        给小部件设置文字。
        :param text: 要显示的小部件文字。
        调用示例：
        await widget.set_text('Hello world!')
        """
        dispatch(self.send_action('setText', to_any(text)))


class VirtualWidget(BaseWidget):
    async def send_action(self, name: str, *argv, session_id: int = 0, future_id: int = 0):
        pass

    async def send_action_until_return(self, name: str, *argv) -> typing.Any:
        pass

    def set_action_result(self, session_id: int, future_id: int, res):
        pass

    def create_callback(self, func: typing.Optional[typing.Callable], *args) -> typing.Union[Any, Empty]:
        pass

    async def destroy(self):
        """
        销毁虚拟小部件。
        """
        pass
